package org.cbio.causality.util;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.biopax.paxtools.controller.Cloner;
import org.biopax.paxtools.controller.Completer;
import org.biopax.paxtools.controller.PathAccessor;
import org.biopax.paxtools.controller.SimpleEditorMap;
import org.biopax.paxtools.io.BioPAXIOHandler;
import org.biopax.paxtools.io.SimpleIOHandler;
import org.biopax.paxtools.model.BioPAXElement;
import org.biopax.paxtools.model.BioPAXLevel;
import org.biopax.paxtools.model.Model;
import org.biopax.paxtools.model.level3.*;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * This class excises a L3 model to the given pathways. The new model contains these new pathways.
 * @author Ozgun Babur
 */
public class ModelExciser
{
	private static Log log = LogFactory.getLog(ModelExciser.class);

	/**
	 * Editor map to use for excising.
	 */
	private static final SimpleEditorMap EM = SimpleEditorMap.L3;

	/**
	 * Accessor to controls of interactions.
	 */
	protected static final PathAccessor cotrolAcc =
		new PathAccessor("Interaction/controlledOf*:Interaction");

	/**
	 * IO handler for reading and writing BioPAX.
	 */
	static BioPAXIOHandler handler =  new SimpleIOHandler();


	public static void exciseToFile(Model model, List<PathwayTicket> tickets, boolean addControls,
		boolean addNumber, String outFile)
	{
		model = excise(model, tickets, addControls, addNumber);
		try
		{
			handler.convertToOWL(model, new FileOutputStream(outFile));
		}
		catch (FileNotFoundException e)
		{
			log.error("Cannot write to file.", e);
		}
	}

	public static Model excise(Model model, List<PathwayTicket> tickets, boolean addControls,
		boolean addNumber)
	{
		Set<BioPAXElement> content = new HashSet<BioPAXElement>();

		int i = 0;
		for (PathwayTicket ticket : tickets)
		{
			if (addControls) ticket.addControls();
			if (addNumber) ticket.addNumberToName(++i, tickets.size());
			content.addAll(ticket.inters);
		}

		Model excised = excise(model, content);

		i = 0;
		for (PathwayTicket ticket : tickets)
		{
			Pathway pathway = excised.addNew(Pathway.class,
				System.currentTimeMillis() + "-AutoGenerated-" + (++i));

			pathway.setDisplayName(ticket.name);
			pathway.addComment(ticket.comment);

			for (Interaction anInt : ticket.inters)
			{
				pathway.addPathwayComponent((org.biopax.paxtools.model.level3.Process)
					excised.getByID(anInt.getRDFId()));
			}
		}

		return excised;
	}

	public static Model excise(Model model, Set<BioPAXElement> content)
	{
		Completer c = new Completer(EM);

		content = c.complete(content, model);

		Cloner cln = new Cloner(EM, BioPAXLevel.L3.getDefaultFactory());

		return cln.clone(model, content);
	}

	public static class PathwayTicket
	{
		Set<Interaction> inters;
		String comment;
		String name;

		public PathwayTicket(String name, String comment, Interaction... inter)
		{
			this.name = name;
			this.comment = comment;
			inters = new HashSet<Interaction>(Arrays.asList(inter));
		}

		public void addControls()
		{
			for (Interaction inter : new HashSet<Interaction>(inters))
			{
				for (Object o : cotrolAcc.getValueFromBean(inter))
				{
					if (o instanceof Control)
					{
						inters.add((Control) o);
					}
				}
			}
		}

		public void addNumberToName(int no, int max)
		{
			this.name = getWithLeadingZeros(no, max) + " " + name;
		}

		/**
		 * Prepares an int for printing with leading zeros for the given size.
		 * @param i the int to prepare
		 * @param size max value for i
		 * @return printable string for i with leading zeros
		 */
		public static String getWithLeadingZeros(int i, int size)
		{
			assert i <= size;
			int w1 = (int) Math.floor(Math.log10(size));
			int w2 = (int) Math.floor(Math.log10(i));

			String s = "";

			for (int j = w2; j < w1; j++)
			{
				s += "0";
			}
			return s + i;
		}
	}
}
